extension MapExtesnion<K, V> on Map<K, V> {
  Map<K, V> where(bool Function(K key, V value) f) => Map<K, V>.fromEntries(entries.where((element) => f(element.key, element.value)));

  Map<K, V> whereKey(bool Function(K key) f) => {...where((key, value) => f(key))};

  Map<K, V> whereValue(bool Function(V value) f) => {...where((key, value) => f(value))};

  Map<R, V> mappedKeys<R>(R Function(K) f) => map((key, value) => MapEntry(f(key), value));

  Map<K, R> mappedValues<R>(R Function(V) f) => map((key, value) => MapEntry(key, f(value)));

  Map<K, V> operator |(Map<K, V> other) => {...this}..addEntries(other.entries);

  Map<K2, V2> compactMap<K2, V2>(MapEntry<K2, V2>? Function(MapEntry<K, V>) f) {
    final result = <K2, V2>{};
    for (final entry in entries) {
      final newEntry = f(entry);
      if (newEntry != null) {
        result[newEntry.key] = newEntry.value;
      }
    }
    return result;
  }
}

extension KeyPath on Map? {
  T valueFor<T>(String keyPath, T defaultValue, {String splitChar = '.'}) {
    final keysSplit = keyPath.split(splitChar).where((element) => element.isNotEmpty).toList();
    if (keysSplit.isEmpty) return defaultValue;
    
    final thisKey = keysSplit.removeAt(0);
    final thisValue = this?[thisKey];
    if (keysSplit.isEmpty) {
      return (thisValue  as T?) ?? defaultValue;
    } else if (thisValue is Map) {
      return thisValue.valueFor(keysSplit.join(splitChar), defaultValue);
    }
    return defaultValue;
  }

  setValue(String keyPath, Object? value, {String splitChar = '.'}) {
    final keysSplit = keyPath.split(splitChar).where((element) => element.isNotEmpty).toList();
    if (keysSplit.isEmpty) return;
    final thisKey = keysSplit.removeAt(0);

    if (keysSplit.isEmpty) {
      this?[thisKey] = value;
      return;
    }

    this?[thisKey] ??= {};
    final thisValue = this?[thisKey];
    if (thisValue is Map) {
      thisValue.setValue(keysSplit.join(splitChar), value);
    }
  }
}
